
#include <swarm.h>
#include <swarmio.h>

#include <ctype.h>
#include <stdlib.h>

/* Routines for managing the symbol table. */

swio_sym_data *swio_sym_make(char *val, int type){

  swio_sym_data *data = (swio_sym_data *)malloc(sizeof(swio_sym_data));
  data->type = type;

  if (type == SYM_NUMERIC){
    /* Try and parse it as a numeric. */
    double *conv = (double *)malloc(sizeof(double));
    int converted = sscanf(val, "%lf", conv);
    if (!converted){ // Error.
      free(data);
      free(conv);
      return NULL;
    }
    data->data = conv;
    return data;
  }

  /* Assume we are using a string here. */
  data->data = val;
  return data;

}

void swio_sym_add(char *name, swio_sym_data *data){

  hash_add(sym_tbl, name, strlen(name), data);

}

swio_sym_data *swio_sym_get(char *name){
  
  hash_elem *elem = hash_get(sym_tbl, name, strlen(name));
  if ( ! elem) return NULL;
  return elem->data;

}

swio_sym_data *swio_sym_set(char *name, swio_sym_data *data){

  hash_elem *tmp = hash_get(sym_tbl, name, strlen(name));
  if (tmp){
    swio_sym_data *prev = (swio_sym_data *) tmp->data;
    tmp->data = data;
    return prev;
  } else {
    swio_sym_add(name, data);
    return NULL;
  }

}

void _swio_print_sym_data(swio_sym_data *data){

  if (data->type == SYM_NUMERIC){
    printf("%lf\n", *((double *)data->data));
  } else {
    printf("%s\n", (char *)data->data);
  }

}

void _swio_print_sym_table(){

  printf("[swarmio] Symbol table:\n");

  hash_iterator *i = hash_new_iterator(sym_tbl);

  hash_elem *elem;
  while ((elem = hash_iterate(i)) != NULL){

    /* Print the elem. */
    char *key_str = (char *) malloc(elem->key->length + 1);
    memcpy(key_str, (char *) elem->key->key, elem->key->length);
    key_str[elem->key->length] = 0;

    printf("[swarmio]   Symbol: hash=0x%08x key=%-12s data=", 
	   elem->key->hash, key_str);
    _swio_print_sym_data(elem->data);
  }

}

inline int _swio_is_symbol(char *str){

  return str[0] == '$' ? 0 : 1;

}

void swio_undefined_symbol(char *sym){

  printf("[swarmio] Nonexistant symbol: %s\n", sym);
  printf("[swarmio]   File: %s, line: %d\n",
	 files->top->name, files->top->current_line);
  sw_exit(1);

}

/**
 * Turns a string into a symbol and possible value. Though there doesn't have
 * to be a value. If an error occurs this dies with a suitable message. This
 * can parse symbols assigned to other symbols, numerics, and strings, 
 */
int swio_symbolize(char *symbol){

  char *name = symbol;
  char *val = symbol;
  char *name_cpy;

  /* First find the name of our symbol. */
  while (*val != '='){
    if (*val == ' ') break;
    if (*val == 0) return 1;
    val++;
  }
  val++;

  name_cpy = (char *)malloc((int)(val-name));
  memcpy(name_cpy, name, (int)(val-name));
  name_cpy[(int)(val-name)-1] = 0;

  if (strlen(name_cpy) < 1){
    return 1;
  }

  // First see if there is an equals in this string
  int equals = 0;
  int i = 0;
  for ( ; i < strlen(val); i++){
    if (val[i] == '='){
      equals = 1;
    }
  }
  if (equals){
    while (*val++ != '=');
  }

  // Now trim of frontal spaces.
  while (isspace(*val)) val++;

  // See if there are quotes.
  if (val[0] == '"'){
    char *tmp = _swio_trim_quotes(val);
    if (!tmp){
      /* No found closing quote. */
      swio_die_miss_matched_quote(val);
    }
    val = tmp;
  }

  /* Now make the symbol. */
  int sym_type;
  double tmp;

  // First see if the symbol is being assigned to another symbol.
  if (_swio_is_symbol(val) == 0){
    char *symbol = val+=1;
    swio_sym_data *tmp = swio_sym_get(symbol);

    if (!tmp){ // This symbol doesnt exist.
      swio_undefined_symbol(symbol);
    }
    
    if (tmp->type == SYM_NUMERIC){
      
      double *new_data = (double *)malloc(sizeof(double));
      *new_data = *(double *)tmp->data;
      
      swio_sym_data *sym_data = (swio_sym_data *)malloc(sizeof(swio_sym_data));
      sym_data->type = tmp->type;
      sym_data->data = new_data;

      swio_sym_set(name_cpy, sym_data);
      
    } else {
      
      char *new_data = (char *)malloc(strlen(tmp->data) + 1);
      memcpy(new_data, tmp->data, strlen(tmp->data) + 1);
      
      swio_sym_data *sym_data = (swio_sym_data *)malloc(sizeof(swio_sym_data));
      sym_data->type = tmp->type;
      sym_data->data = new_data;

      swio_sym_set(name_cpy, sym_data);
      
    }
    return 0;
  }

  sym_type = sscanf(val, "%lf", &tmp);
  swio_sym_data *data = swio_sym_make(val, sym_type);

  if (!data){
    return 1;
  }

  swio_sym_set(name_cpy, data);

  return 0;

}
